package com.gwm.search.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.gwm.common.utils.BeanHelper;
import com.gwm.common.utils.JsonUtils;
import com.gwm.common.vo.PageResult;
import com.gwm.item.client.ItemClient;
import com.gwm.item.dto.*;
import com.gwm.search.dto.GoodsDTO;
import com.gwm.search.dto.SearchRequest;
import com.gwm.search.entity.Goods;
import com.gwm.search.repository.GoodsRepository;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SearchService {


    @Autowired()
    private ItemClient itemClient;
    @Autowired
    private ElasticsearchTemplate esTemplate;


    /**
     * 把一个Spu转为一个Goods对象
     *
     * @param spu
     * @return
     */
    public Goods buildGoods(SpuDTO spu) {

        Goods goods = new Goods();

        goods.setBrandId(spu.getBrandId());
        goods.setCategoryId(spu.getCid3());
        goods.setCreateTime(spu.getCreateTime().getTime());
        goods.setId(spu.getId());
        goods.setSubTitle(spu.getSubTitle());

        // 1 商品相关搜索信息的拼接：名称、分类、品牌等
        //名称,分类，品牌 等，完成拼接
        String all = spu.getName()+spu.getBrandName()+spu.getCategoryName();
        goods.setAll(all); //商品名称+品牌名称+分类名称

//        spu.getSkus()
        // 2 spu下的所有sku的JSON数组
        List<SkuDTO> skuList = itemClient.findSkuListBySpuId(spu.getId());
        Set<Long> price = skuList.stream().map(SkuDTO::getPrice).collect(Collectors.toSet());
        goods.setPrice(price); //当前spu下所有sku的价格（不重复）


      List<Map<String,Object>> skus = new ArrayList();
        for (SkuDTO skuDTO : skuList) {
            Map map = new HashMap();
            map.put("id",skuDTO.getId());
            map.put("title", skuDTO.getTitle());
            map.put("price",skuDTO.getPrice());
            if(StringUtils.isNotBlank(skuDTO.getImages())){
              map.put("image",StringUtils.substringBefore(skuDTO.getImages(),",")); //sku中的第一张图片
            }
            skus.add(map);
        }
        String skusStr = JsonUtils.toString(skus);
        goods.setSkus(skusStr); //


        Map<String, Object> specMap = new HashMap();
//        这个map中需要多少对数据 取自于这个商品对应的分类有多少需要查询的规格参数

//        查询当前分类中的用来查询的规格参数
        List<SpecParamDTO> paramList = itemClient.findSpecParamByCategoryIdOrGroupId(spu.getCid3(), null, true);
//        paramList[{id:10,name;"CPU核数"},{id:11,name;"CPU频率"},{id:13,name:"机身存储"}]

//        需要查询SPUDetail
        SpuDetailDTO spuDetail = itemClient.findSpuDetailBySpuId(spu.getId());
        String genericSpec = spuDetail.getGenericSpec(); //        {"8":"骁龙","9":"骁龙865","10":"8","11":"2.3"}
        Map<Long,String> genericMap = JsonUtils.toMap(genericSpec, Long.class, String.class);


        String specialSpec = spuDetail.getSpecialSpec(); //        {"4":["陶瓷黑","亮银版"],"12":["8G"],"13":["128G","256G","512G"]}
        Map<Long, List<String>> specialMap = JsonUtils.nativeRead(spuDetail.getSpecialSpec(), new TypeReference<Map<Long, List<String>>>() {});


        for (SpecParamDTO param : paramList) {
            String key = param.getName();
            Object value = null;
            if (param.getGeneric()){
                value = genericMap.get(param.getId());
            }else{
                value = specialMap.get(param.getId());
            }

            if (param.getIsNumeric()){
//                2.3----->2.0-2.5GHz
                value = chooseSegment(value, param);
            }
            specMap.put(key,value);
        }
//        specMap.put("规格参数名称"，当前spu中的规格参数值)
        goods.setSpecs(specMap);

        return goods;
    }

//    2.3------>"2.0-2.5"

    private String chooseSegment(Object value, SpecParamDTO p) {
        if (value == null || StringUtils.isBlank(value.toString())) {
            return "其它";
        }
        double val = parseDouble(value.toString());
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = parseDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if (segs.length == 2) {
                end = parseDouble(segs[1]);
            }
            // 判断是否在范围内
            if (val >= begin && val < end) {
                if (segs.length == 1) {
                    result = segs[0] + p.getUnit() + "以上";
                } else if (begin == 0) {
                    result = segs[1] + p.getUnit() + "以下";
                } else {
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

    private double parseDouble(String str) {
        try {
            return Double.parseDouble(str);
        } catch (Exception e) {
            return 0;
        }
    }

//    根据关键字查询
    public PageResult<GoodsDTO> findByPage(SearchRequest searchRequest) {
        Integer page = searchRequest.getPage(); //当前页码
        Integer pageSize = searchRequest.getSize();

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        basicQuery(queryBuilder, searchRequest); //调用拼凑基本的查询条件
//        "includes": ["id","subTitle","skus"]
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","subTitle","skus"},null));
        queryBuilder.withPageable(PageRequest.of(page-1,pageSize, Sort.by(Sort.Direction.DESC,"createTime")));



        AggregatedPage<Goods> queryForPage = esTemplate.queryForPage(queryBuilder.build(), Goods.class);
        long total = queryForPage.getTotalElements();
        int totalPages = queryForPage.getTotalPages();

        List<Goods> goodsList = queryForPage.getContent();
        List<GoodsDTO> goodsDTOList = BeanHelper.copyWithCollection(goodsList, GoodsDTO.class);
//        Long total, Long totalPage, List<T> items
        return new PageResult<GoodsDTO>(total,(long)totalPages,goodsDTOList);

    }

    public Map<String, List<?>> filter(SearchRequest searchRequest) {

        Map<String, List<?>> resultMap = new HashMap<>();

        NativeSearchQueryBuilder queryBuilder =  new NativeSearchQueryBuilder();

        basicQuery(queryBuilder, searchRequest); //调用拼凑基本的查询条件
//        为了提高查询效率 显示的结果要简洁
        queryBuilder.withPageable(PageRequest.of(0,1)); //这里的size一定要大于0

//        构建根据品牌聚合条件
        queryBuilder.addAggregation(AggregationBuilders.terms("brandAgg").field("brandId").size(100));
//        构建根据分类聚合条件
        queryBuilder.addAggregation(AggregationBuilders.terms("categoryAgg").field("categoryId").size(100));

//        执行查询
        AggregatedPage<Goods> queryForPage = esTemplate.queryForPage(queryBuilder.build(), Goods.class);
        Aggregations aggregations = queryForPage.getAggregations();

//        处理品牌的聚合结果 会把结果放到 resultMap
        handlerBrandAgg(resultMap, aggregations);
//        处理分类的聚合结果 会把结果放到 resultMap
        List<Long> categoryIdList = handlerCategoryAgg(resultMap, aggregations);

        if(categoryIdList!=null&&categoryIdList.size()>0){
            handlerSpecsAgg(categoryIdList,searchRequest,resultMap);
        }


        return resultMap;
    }

    private void handlerSpecsAgg(List<Long> categoryIdList,SearchRequest searchRequest,Map<String, List<?>> resultMap) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        basicQuery(queryBuilder, searchRequest); //调用拼凑基本的查询条件
//        为了提高查询效率 显示的结果要简洁
        queryBuilder.withPageable(PageRequest.of(0,1)); //这里的size一定要大于0

        Long categoryId = categoryIdList.get(0);
//            开搞规格参数的聚合
        List<SpecParamDTO> paramList = itemClient.findSpecParamByCategoryIdOrGroupId(categoryId, null, true);
        for (SpecParamDTO param : paramList) {
            queryBuilder.addAggregation(AggregationBuilders.terms(param.getName()+"Agg").field("specs."+param.getName()).size(100));
        }
//        执行查询
        AggregatedPage<Goods> aggregatedPage = esTemplate.queryForPage(queryBuilder.build(), Goods.class);
        Aggregations aggregations = aggregatedPage.getAggregations();


        for (SpecParamDTO param : paramList) {
            Terms specTerms = aggregations.get(param.getName() + "Agg");
            List<? extends Terms.Bucket> buckets = specTerms.getBuckets();
            List<String> collect = buckets.stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
            resultMap.put(param.getName(),collect);
        }
    }

    private void basicQuery(NativeSearchQueryBuilder queryBuilder, SearchRequest searchRequest) {
//        queryBuilder.withQuery(QueryBuilders.matchQuery("all", searchRequest.getKey()));

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.matchQuery("all", searchRequest.getKey()));

        Map<String, String> filterMap = searchRequest.getFilterMap();

        for (String key : filterMap.keySet()) {

            if(key.equals("分类")){
                boolQueryBuilder.filter(QueryBuilders.termQuery("categoryId",filterMap.get(key)));
            }else if(key.equals("品牌")){
                boolQueryBuilder.filter(QueryBuilders.termQuery("brandId",filterMap.get(key)));
            }else {
                boolQueryBuilder.filter(QueryBuilders.termQuery("specs."+key,filterMap.get(key)));
            }
        }
        queryBuilder.withQuery(boolQueryBuilder);
    }

    private void handlerBrandAgg(Map<String, List<?>> resultMap, Aggregations aggregations) {
        Terms brandTerms = aggregations.get("brandAgg");
        List<? extends Terms.Bucket> brandBuckets = brandTerms.getBuckets();
        List<Long> brandIdList = brandBuckets.stream().map(Terms.Bucket::getKeyAsNumber).map(Number::longValue).collect(Collectors.toList());
        List<BrandDTO> brandList = itemClient.findBrandListByBrandIdList(brandIdList);
        resultMap.put("品牌",brandList);
    }
    private List<Long> handlerCategoryAgg(Map<String, List<?>> resultMap, Aggregations aggregations) {
        Terms brandTerms = aggregations.get("categoryAgg");
        List<? extends Terms.Bucket> brandBuckets = brandTerms.getBuckets();
        List<Long> categoryIdList = brandBuckets.stream().map(Terms.Bucket::getKeyAsNumber).map(Number::longValue).collect(Collectors.toList());
        List<CategoryDTO> categoryList = itemClient.findCategoryListByIds(categoryIdList);
        resultMap.put("分类",categoryList);

        return  categoryIdList;
    }

    @Autowired
    private GoodsRepository goodsRepository;

    public void deleteIndexById(Long spuId) {
        goodsRepository.deleteById(spuId);
    }

    public void createOneIndex(Long spuId) {

        SpuDTO spu = itemClient.findSpuBySpuId(spuId);
//        注意这里的spu中的brandName和CategoryName值是空的
        String brandName = itemClient.findBrandByBrandId(spu.getBrandId()).getName();
        spu.setBrandName(brandName);

        List<CategoryDTO> categoryList = itemClient.findCategoryListByIds(spu.getCategoryIds());
        String categoryNames = categoryList.stream().map(CategoryDTO::getName).collect(Collectors.joining("/"));
        spu.setCategoryName(categoryNames);

        Goods goods = buildGoods(spu);
        goodsRepository.save(goods);
    }
}
